#include <stdlib.h>
#include <iostream>  
#include <sstream>  
#include <string>
#include <cstring>
#include <fstream>
#include <dirent.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/reg.h>
#include <sys/types.h>
#include <unistd.h>
using namespace std;


#define MB 1048576
#define COMPILE_TIME 6
#define COMPILE_MEM 128
#define FILE_SIZE 10

#define LAN_C 1
#define LAN_CPP 2
#define LAN_JAVA 3
#define TOTAL_LAN 3


#define OJ_AC 1   //答案正确 Accepted 
#define OJ_WA 2   //答案错误 Wrong Answer
#define OJ_TLE 3   //超时 Time Limit Exceeded
#define OJ_MLE 4   //超内存 Memory Limit Exceeded
#define OJ_RE 5    //运行时错误 Runtime Error
#define OJ_OLE 6   //输出超限制 Output Limit Exceeded
#define OJ_CE 7    //编译错误 Compile Error
#define OJ_SE 8    //系统错误 System Error
#define OJ_QU 9   //排队Queueing
#define OJ_CL 10  //正在编译 Compiling
#define OJ_RN 11   //正在运行 Running

#define OFFSET_OLE 1024

// C and C++ 列举可以使用的系统调用，如果某代码执行的内容超出这些，就没有权限，返回“运行时错误”
const int ALLOW_SYS_CALL_C[] = {0,1,2,3,4,5,8,9,11,12,20,21,59,63,89,158,231,240, SYS_time, SYS_read, SYS_uname, SYS_write
	, SYS_open, SYS_close, SYS_execve, SYS_access, SYS_brk, SYS_munmap, SYS_mprotect, SYS_mmap, SYS_fstat
	, SYS_set_thread_area, 252, SYS_arch_prctl, 0 };
// java
const int ALLOW_SYS_CALL_JAVA[] = { 0,2,3,4,5,9,10,11,12,13,14,21,56,59,89,97,104,158,202,218,231,273,257,
		61, 22, 6, 33, 8, 13, 16, 111, 110, 39, 79, SYS_fcntl, SYS_getdents64, SYS_getrlimit, SYS_rt_sigprocmask
		, SYS_futex, SYS_read, SYS_mmap, SYS_stat, SYS_open, SYS_close, SYS_execve, SYS_access, SYS_brk, SYS_readlink
		, SYS_munmap, SYS_close, SYS_uname, SYS_clone, SYS_uname, SYS_mprotect, SYS_rt_sigaction, SYS_getrlimit
		, SYS_fstat, SYS_getuid, SYS_getgid, SYS_geteuid, SYS_getegid, SYS_set_thread_area, SYS_set_tid_address
		, SYS_set_robust_list, SYS_exit_group, 158, 0 };

const char* SYSTEM_ERROR_LOG = "systemError.log";
bool allowSysCall[512];



int compile(int lang, const char* ceFile,const char* filepath, int runId); //编译程序
void updateCompileErrorInfo(int runId, const char* ceFile); //更新编译出错信息
void updateSubmitStatusRight(int flag, int result, int time, int mem); //更新提交状态（成功的情况）
void updateSubmitStatusError(int flag, int result, const char* ceInfo); //更新提交状态（出错的情况）
void judge(const char* casepath,int lang); //判题部分
void run(int lang, int timeLimit, int memLimit, int& usedTime, const char* dataIn, const char* userOut, const char* errOut);   //运行程序
void watchRunningStatus(pid_t pidRun, const char* errFile, int lang, int& result, int& topMem,int& usedTime, int memLimit, int timeLimit, const char* userOut, int outputLen); //查看运行状态
int getProcStatus(int pid, const char* statusTItle);  //获取进程信息
int getFileSize(const char* fileName); //获取文件大小
int checkResult(const char* DataOutFileName, const char* userOutFileName,int n);
//对比检查输出结果
int readCasenum(const char* path); //读取文件夹内测试案例文件数量
int readFilenum(const char* path); //读取文件夹内.cpp源文件数量
char **readFileFolder(int type,const char* path); //读取测试案例文件夹名称
char **readSourceFileName(int lang,const char* path); //读取源文件名称
void initAllowSysCall(int lang);  //初始化允许的系统调用

int main(int argc,char *argv[]) {
	   // 写文件  

int result = OJ_AC;
      // char* testcasepath1=argv[1];
       char* sourcefilepath=argv[1];
       char* name=argv[2];
       char* testcasepath="/home/song/Desktop/gitee/homework1_problem1/test/";
       char* sourcefilepath2="/home/song/Desktop/oj/sourcefiles/";
     //  cout<<"argv[0]: "<<argv[0]<<"  argv[1]:"<<argv[1]<<"  argv[2]:"<<argv[2]<<endl;
	const char* ceFile = "/home/song/Desktop/gitee/homework1_problem1/ce.dat"; // 负责记录的文件
	  int status = compile(2, ceFile,sourcefilepath, 1);  //执行编译，返回一个状态值status
       cout<<"compile pid status: "<<status<<endl;
       
	if (status == -1) {// system error -1为系统错误，更新提交信息（4个参数）
        updateSubmitStatusError(-1, OJ_SE, nullptr);  

	} else if (status != 0) {// compile error  不为0是编译出错，更新编译错误信息，读取ceFile文件里记录的信息
		updateSubmitStatusError(0,OJ_CE,ceFile);
	} else {// compile success  编译成功，进行判题
cout<<"compile status: "<<status<<endl;

   ofstream outFile;
if (!outFile)
{
cout<<"no file"<<endl;
}
else{
    outFile.open("result.csv",ios::app); // 打开模式可省略  
} 
outFile<<"name: "<<name<<endl;
outFile<<"compile status: "<<status<<endl;
outFile.close(); 

updateSubmitStatusError(1,OJ_CE,ceFile);

		judge(testcasepath,2);

	}
 
      
	return 0;
}

int compile(int lang, const char* ceFile,const char* filepath, int runId)// 参数是：语言/ceFile文件、运行ID
{
        int i,j;
	int time_limit = COMPILE_TIME;  //规定执行子进程的时间限制为6
	int memory_limit = COMPILE_MEM*MB; // 规定内存限制大小为128

	// compile commands
	// c and c++ must use --static to compile, otherwise the memory statistics will be incorrect
	const char *COMP_C[] = {"gcc","-Wall","-lm", "--static","Main.c","-o","Main"};//子进程执行编译的命令语句
	// const char *COMP_CPP[] = {"g++","-Wall","-fno-asm","-lm", "--static", "-std=c++11","sourcefiles/Main.cpp","sourcefiles/calculate.cpp","-o","Main"};
     
int filenum;
char **filename=readSourceFileName(lang,filepath); 
filenum=readFilenum(filepath);
cout<<"filenum: "<<filenum<<endl;
for(j=0;j<filenum;j++)
{
cout<<"filename: "<<filename[j]<<endl;
}

     const char *COMP_CPP2[20];
         COMP_CPP2[0] = "g++";
         COMP_CPP2[1]="-Wall";
         COMP_CPP2[2]="-fno-asm";
         COMP_CPP2[3]="-lm";
         COMP_CPP2[4]="--static";
         COMP_CPP2[5]= "-std=c++11";
for(i=0;i<filenum;i++)
{
        COMP_CPP2[i+6]=filename[i];
}       
        COMP_CPP2[filenum+6]="-o";
        COMP_CPP2[filenum+7]="/home/song/Desktop/gitee/homework1_problem1/Main";
    
	pid_t pid = fork();  //子进程
	if (pid == -1){    //pid=-1时，无法开启子进程，输出出错原因，返回-1
		// system error
		std::ofstream fout(SYSTEM_ERROR_LOG, std::ios_base::out | std::ios_base::app);
		fout << "Error: fork() file when compile.\n";
		fout << "In file " << __FILE__ << " function (" << __FUNCTION__ << ")"
			<<" , line: " << __LINE__ << "\n";
		fout.close();
		return -1;
	} else if (pid != 0) {// parent   pid=0 是父进程，需要等待子进程执行完毕再执行父进程，并返回子进程的执行状态status
        int status;
        waitpid(pid, &status, 0);

		return status;
 	} else {// child   //pid不为-1、不为0时，是子进程
		/* set rlimit*/
		struct rlimit lim;   //结构体rlimit来限制运行时间、内存
		lim.rlim_cur = lim.rlim_max = time_limit;  //RLIMIT_CPU 是CPU运行时间（秒）：cur为软限制、max为硬限制
		setrlimit(RLIMIT_CPU, &lim);

		alarm(0);  //重置闹钟
		alarm(time_limit);//如果达到指定时间，就强制退出子进程

	//	lim.rlim_max =
		lim.rlim_cur = memory_limit;   //RLIMIT_AS 限制进程运行的虚拟内存的最大字节长度
		setrlimit(RLIMIT_AS, &lim);

		lim.rlim_cur = lim.rlim_max = FILE_SIZE*MB;    //RLIMIT_FSIZE 限制进程创建的文件的最大字节长度
    	setrlimit(RLIMIT_FSIZE, &lim);

		/* start compile */
		freopen(ceFile,"w",stderr);  //Compile Error写入文件
		switch(lang){  //根据语言类型，选择执行的编译指令
			case LAN_C: execvp(COMP_C[0],(char* const*)COMP_C); break;
			case LAN_CPP: execvp(COMP_CPP2[0],(char* const*)COMP_CPP2); break;
			default: std::cout << "Nothing to do.\n";
		}

		exit(0);   //子进程退出，（退出时status为0 ）？？
	}
}

void judge(const char* casepath, int lang) //判题部分
{
int filenum,casenum;
int j;
casenum=readCasenum(casepath);
  ///////////// cout<<"casenum: "<<casenum/2<<endl;
char **in2=readFileFolder(1,casepath);
for(j=0;j<casenum/2;j++)
{
 ///////////  cout<<"in2: "<<in2[j]<<endl;
}

char **out2=readFileFolder(2,casepath);
for(j=0;j<casenum/2;j++)
{
 ///////////   cout<<"out2: "<<out2[j]<<endl;
}


	const char* dataIn = "data.in";  //定义几个文件，存放 输入数据、用户输出、错误输出
	const char* userOut = "/home/song/Desktop/gitee/homework1_problem1/user.out";
	const char* dataOut = "/home/song/Desktop/gitee/homework1_problem1/data.out";
	const char* errOut = "/home/song/Desktop/gitee/homework1_problem1/err.out";

	std::ifstream fin(dataOut);
	std::string line;
	std::string dataOutcontent = "";

	while (getline(fin,line)) {
		dataOutcontent += line+"\n";
	}

	fin.close();

	/* get time limit and memory limit of this problem */
	int timeLimit = 1, memLimit = 64;
	/* start judge */
	int result = OJ_AC;  //初始化判题信息：结果为正确、运行时间、内存为0
	int maxTime = 0;		
        int topMem = 0;
	initAllowSysCall(lang);  //系统调用，和语言有关？
        int i=0;
for(i=0;i<casenum/2;i++)    
 {
       int usedTime = 0;  //初始化用户运行时间
	pid_t pidRun = fork();   //开子进程
	if (pidRun == 0) {  //pid为0, 在子进程中，运行程序
	// run the submit program
	run(lang, timeLimit, memLimit, usedTime, in2[i], userOut, errOut);
//参数有：语言、运行时间限制、运行内存限制、初始化用户运行时间、输入数据、用户输出文件、错误输出文件
	exit(0);
   
	} 
       else if (pidRun != -1) { //pid不为-1，是父进程，查看运行状态
// watch the running process, and update the result, max memory and time
	watchRunningStatus(pidRun, errOut, lang, result, topMem, usedTime, memLimit, timeLimit, userOut, dataOutcontent.length());//参数有：运行ID、错误输出文件、语言、结果、初始化运行内存、初始化用户运行时间、运行时间限制、内存限制、用户输出文件、标准输出数据长度
   //cout<<pidRun<<endl;
	if (maxTime < usedTime) 
       maxTime = usedTime;
	} 
     else {// system error
	std::ofstream fout(SYSTEM_ERROR_LOG, std::ios_base::out | std::ios_base::app);
	fout << "# ERR fork in " << __FILE__;
	fout << "function is (" << __FUNCTION__ << ") in line " << __LINE__ << "\n";
	fout.close();

	result = OJ_SE;
	}

 
	result = checkResult(out2[i], userOut,i); //检查输出结果
cout<<"testcase: "<<i+1<<": result: "<<result<<endl;	
       
    
	   	updateSubmitStatusRight(i+1, result, maxTime, topMem>>20);
}

}

char **readSourceFileName(int lang,const char* path) //读取源文件名称
{
    char* newpath=new char[50];
    strcpy(newpath, path);
char **namelist;    
DIR *dp;
struct dirent *dirp;
int n=0,i,j,flagname=0;
namelist=(char **)malloc(20*sizeof(char *));
	for (i=0;i<20;i++)
		namelist[i]=(char *)malloc(256*sizeof(char));

if((dp=opendir(path))==NULL)
{
}
   
while (((dirp=readdir(dp))!=NULL) && (n<=50))
{
int i,j;

if(memcmp(".",dirp->d_name,1)==0)   //忽略.和..文件夹
{
}
else
{
    for(i=0,j=strlen(dirp->d_name)-1;i<j;i++,j--){  //文件名逆序
        char c=dirp->d_name[i];  
        dirp->d_name[i]=dirp->d_name[j];  
        dirp->d_name[j]=c;  
  }
if(memcmp(dirp->d_name,"p",1)==0)   //文件名结尾是p，保存下来
{
    for(i=0,j=strlen(dirp->d_name)-1;i<j;i++,j--){  //文件名逆序
        char c=dirp->d_name[i];  
        dirp->d_name[i]=dirp->d_name[j];  
        dirp->d_name[j]=c; 
}
memcpy(namelist[flagname],dirp->d_name,20);  //将.cpp文件名保存到namelist[]
    strcat(newpath, namelist[flagname]);
    strcpy(namelist[flagname],newpath);
    strcpy(newpath, path);
flagname++;
}
}
n++;
}
closedir(dp);
return namelist;

}
char **readFileFolder(int type,const char* path)//读取文件夹测试案例文件名
{
int flagin=0,flagout=0;
char **in,**out;
int i,j;
char temp[60];
    char* newpath=new char[50];
    strcpy(newpath, path);

DIR *dp;
struct dirent *dirp;
int n=0;

in=(char **)malloc(20*sizeof(char *));
	for (i=0;i<20;i++)
		in[i]=(char *)malloc(256*sizeof(char));
out=(char **)malloc(20*sizeof(char *));
	for (i=0;i<20;i++)
		out[i]=(char *)malloc(256*sizeof(char));
if((dp=opendir(path))==NULL)
{
}
   
while (((dirp=readdir(dp))!=NULL) && (n<=50))
{
int i,j;
if(memcmp(".",dirp->d_name,1)==0)   //忽略.和..文件夹
{
}
else
{
    for(i=0,j=strlen(dirp->d_name)-1;i<j;i++,j--){   //文件名逆序
        char c=dirp->d_name[i];  
        dirp->d_name[i]=dirp->d_name[j];  
        dirp->d_name[j]=c;  
  }

if(type==1)
{

if(memcmp(dirp->d_name,"n",1)==0)  //文件名最后一位是n, xxx.in
{
    for(i=0,j=strlen(dirp->d_name)-1;i<j;i++,j--){    //文件名逆序
        char c=dirp->d_name[i];  
        dirp->d_name[i]=dirp->d_name[j];  
        dirp->d_name[j]=c; 
}
memcpy(in[flagin],dirp->d_name,10);   //将文件名写入in[]
    strcat(newpath, in[flagin]);
    strcpy(in[flagin],newpath);
    strcpy(newpath, path);
flagin++;
}
}

if(type==2)
{

if(memcmp(dirp->d_name,"t",1)==0)
{
    for(i=0,j=strlen(dirp->d_name)-1;i<j;i++,j--){  
        char c=dirp->d_name[i];  
        dirp->d_name[i]=dirp->d_name[j];  
        dirp->d_name[j]=c; 
}
memcpy(out[flagout],dirp->d_name,10);
    strcat(newpath, out[flagout]);
    strcpy(out[flagout],newpath);
    strcpy(newpath, path);
flagout++;
}
}

}
n++;
}

closedir(dp);

if(type==1)
{
    for(i=0;i<flagin-1;i++)
	{
		for(j=i+1;j<flagin;j++)   //为测试案例文件排序，顺序为1,2,3.in
		{
		if (strcmp(in[i],in[j])>0)
		{
			memcpy(temp,in[i],60);
			memcpy(in[i],in[j],60);
			memcpy(in[j],temp,60);
		}			
		}
	}
    return in;
}
else
{
    for(i=0;i<flagout-1;i++)
	{
		for(j=i+1;j<flagout;j++)
		{
		if (strcmp(out[i],out[j])>0)
		{
			memcpy(temp,out[i],60);
			memcpy(out[i],out[j],60);
			memcpy(out[j],temp,60);
		}			
		}
	}
    return out;
}
}

int readFilenum(const char* path) //读取文件夹内.cpp源文件数量
{
DIR *dp;
struct dirent *dirp;
int n=0,i,j;
if((dp=opendir(path))==NULL)
return -1; //-1表示未正常打开文件夹

while (((dirp=readdir(dp))!=NULL) && (n<=50))
{
if(memcmp(".",dirp->d_name,1)==0)
{
}
else
{
 for(i=0,j=strlen(dirp->d_name)-1;i<j;i++,j--){  //文件名逆序
        char c=dirp->d_name[i];  
        dirp->d_name[i]=dirp->d_name[j];  
        dirp->d_name[j]=c;  
  }
if(memcmp(dirp->d_name,"p",1)==0)  //结尾是p的要计数，xxx.cpp
{
   n++;
}
}
}

closedir(dp);

return n;

}

int readCasenum(const char* path) //读取文件夹内测试案例文件数量
{
DIR *dp;
struct dirent *dirp;
int n=0;
if((dp=opendir(path))==NULL)
return -1; //-1表示未正常打开文件夹

while (((dirp=readdir(dp))!=NULL) && (n<=50))
{
if(memcmp(".",dirp->d_name,1)==0)  //把.和..文件夹忽略
{
}
else
{
n++;
}
}

closedir(dp);

return n;

}
void updateCompileErrorInfo(int runId, const char* ceFile){  //传入运行ID，ceFile文件
	std::ifstream fin(ceFile);  //ifstream 是C++的读文件操作
	std::string ceStr = "";
	std::string line;
	while (getline(fin,line)) {
		ceStr += line; //将ceFile文件里的内容读到ceStr字符串里
	}
	fin.close();
}


void updateSubmitStatusError(int flag, int result, const char* ceInfo)//更新提交状态（失败的情况）
{
       if(flag==-1)  //系统错误
	   {
		cout<<"flag=-1"<<result<<endl<<endl;
	   }
	   if(flag==0||flag==1)  //编译失败
	   {
		  std::ifstream fin(ceInfo);  //ifstream 是C++的读文件操作
	     std::string ceStr = "";
	    std::string line;
	     while (getline(fin,line)) {
		ceStr += line; //将ceFile文件里的内容读到ceStr字符串里
     	}
	    fin.close();
	  //  cout<<"flag=0 result="<<result<<"  details: "<<ceStr<<endl; //打印编译出错信息
    ofstream outFile;
if (!outFile)
{
cout<<"no file"<<endl;
}
else{
    outFile.open("result.csv",ios::app); // 打开模式可省略  
} 

outFile<<"compile info: "<<ceStr<<endl;
outFile.close();



}
}

void updateSubmitStatusRight(int flag, int result, int time, int mem) //更新提交状态（成功的情况）
{
	//    cout<<flag<<"   time:"<<time<<"   mem:"<<mem<<endl;
}
void run(int lang, int timeLimit, int memLimit, int& usedTime, const char* dataIn
		, const char* userOut, const char* errOut)
//参数有：语言、运行时间限制、运行内存限制、初始化用户运行时间、输入数据、用户输出文件、错误输出文件		
	{
	freopen(dataIn, "r", stdin);    //读输入数据、写输出数据和出错数据
	freopen(userOut, "w", stdout);
	freopen(errOut, "w", stderr);

	ptrace(PTRACE_TRACEME, 0, nullptr, nullptr);  //本进程被其父进程所跟踪。其父进程应该希望跟踪子进程

	// setrlimit
	struct rlimit lim;

	lim.rlim_cur = lim.rlim_max = timeLimit/1000.0+1;// the unit of timeLimit is ms把运行时间转化为毫秒
	setrlimit(RLIMIT_CPU, &lim);  //RLIMIT_CPU CPU时间的使用限制
	alarm(0);
	alarm(timeLimit/1000.0+1);

	lim.rlim_max = (FILE_SIZE<<20) + MB;// the unit of FILE_SIZE is MB 运行内存转换为MB
	lim.rlim_cur = FILE_SIZE<<20;
	setrlimit(RLIMIT_FSIZE, &lim); //RLIMIT_FSIZE 进程所能创建的文件的最大字节长度

	switch (lang) {
		case 3 : // java
			lim.rlim_cur = lim.rlim_max = 80; break;
		default :
			lim.rlim_cur = lim.rlim_max = 1;
	}
	setrlimit(RLIMIT_NPROC, &lim);  // RLIMIT_NPROC 用户可拥有的最大进程数

	lim.rlim_cur = lim.rlim_max = MB << 6;
	setrlimit(RLIMIT_STACK, &lim);  //最大的进程堆栈

	if (lang < 3) {
		lim.rlim_cur = memLimit * MB / 2 * 3;// the unit of memLimit is MB
		lim.rlim_max = memLimit * MB;
		setrlimit(RLIMIT_AS, &lim);  //RLIMIT_AS 进程虚拟内存（地址空间，Address Space）的最大字节长度
	}

	switch (lang) {
		case 1: case 2:
		execl("/home/song/Desktop/gitee/homework1_problem1/Main", "/home/song/Desktop/gitee/homework1_problem1/Main", nullptr); break;  //C和C++执行 可执行文件Main

	}
}


int getProcStatus(int pid, const char* statusTitle) {// get memory info from /proce/pid/status
	char file[64];
	sprintf(file, "/proc/%d/status", pid);
	std::ifstream fin(file);
	std::string line;

	int sLen = strlen(statusTitle);

	int status = 0;// the unit of status is KB
	while (getline(fin, line)) {
//		std::cout << line << "\n";

		int lLen = line.length();
		if (lLen <= sLen) continue;

		// find line
		bool flag = true;
		for (int i=0; i<sLen; ++i) {
			if (line[i] != statusTitle[i]) {
				flag = false;
				break;
			}
		}

		if (flag) {
			// get status
			for (int i=sLen; i<lLen; ++i){
				if (line[i]>='0' && line[i]<='9') status = status*10 + line[i]-'0';
				else if (status) break;
			}
			break;
		}
	}

//	std::cout << "\n";

	return status;
}


void watchRunningStatus(pid_t pidRun, const char* errFile, int lang, int& result, int& topMem, int& usedTime, int memLimit, int timeLimit, const char* userOut, int outputLen) {
	// the unit of memLimit is MB, the unit of timeLimit is ms
//参数：运行ID、错误输出文件、语言、结果、初始化运行内存、初始化用户运行时间、运行时间限制、内存限制、用户输出文件、标准输出数据长度

	int tmpMem = 0;
	int status, sig, exitCode; //定义状态、信号、退出码
	struct rusage usage; //进程资源信息 结构体

	if (topMem == 0)  //最大内存限制
	topMem = getProcStatus(pidRun, "VmRSS:")<<10; //VmRSS：进程当前使用的物理内存的大小
	wait(&status);// wait execl
	if (WIFEXITED(status)) return;  //WIFEXITED()宏用来判断子进程是否为正常退出的，正常退出会返回一个非零值
//若上宏为真，此时可通过WEXITSTATUS(status)获取进程退出状态(exit时参数) 
	ptrace(PTRACE_SETOPTIONS, pidRun, nullptr, PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXIT | PTRACE_O_EXITKILL);
	//跟踪多线程，通过第一个参数来设置，第四个参数为指令
	
	ptrace(PTRACE_SYSCALL, pidRun, nullptr, nullptr);//继续执行。pid表示被跟踪的子进程
	//ptrace(PTRACE_SYS, pid, 0, signal),signal为0则忽略引起调试进程中止的信号，若不为0则继续处理信号signal。

//	topMem = getProcStatus(pidRun, "VmRSS:")<<10;

	while (true) {
		wait4(pidRun, &status, __WALL, &usage);

		/* update topMem and result */
	tmpMem = getProcStatus(pidRun, "VmPeak:") << 10;// the unit of function is KB VmPeak:进程所使用的虚拟内存的峰值

		if (tmpMem > topMem) topMem = tmpMem;
		if (topMem > (memLimit<<20)) {
		if (result == OJ_AC) result = OJ_MLE;  //内存超出限制？？？？？
		ptrace(PTRACE_KILL, pidRun, nullptr, nullptr); //ptrace(PTRACE_KILL,pid)杀掉子进程，使它退出。pid表示被跟踪的子进程。
		break;
		}

		if (WIFEXITED(status)) break;  //WIFEXITED()宏用来判断子进程是否为正常退出的，正常退出会返回一个非零值
		if (getFileSize(errFile)!=0) {
//			std::cout << "RE in line " << __LINE__ << "\n";

		if (result == OJ_AC) result = OJ_RE;
		ptrace(PTRACE_KILL, pidRun, nullptr, nullptr);
		break;
		}

		if (getFileSize(userOut) > outputLen*2+OFFSET_OLE){
			if (result == OJ_AC) result = OJ_OLE;
			ptrace(PTRACE_KILL, pidRun, nullptr, nullptr);
			break;
		}

		exitCode = WEXITSTATUS(status);  //WEXITSTATUS(status)获取进程退出状态(exit时参数) 
		if ( !((lang==3 && exitCode==17) || exitCode==0 || exitCode==133 || exitCode==5) ) {
//			std::cout << "error in line " << __LINE__ << ", exitCode is " << exitCode << "\n";

			if (result == OJ_AC) {
				switch (exitCode) {
					case SIGCHLD :
					case SIGALRM :
						alarm(0);
					case SIGKILL :
					case SIGXCPU :
						result = OJ_TLE;
						break;
					case SIGXFSZ :
						result = OJ_OLE;
						break;
					default :
						result = OJ_RE;
				}
			}
			ptrace(PTRACE_KILL, pidRun, nullptr, nullptr);
			break;
		}

		if (WIFSIGNALED(status)) {   //为非0 表明进程异常终止。 
//若上宏为真，此时可通过WTERMSIG(status)获取使得进程退出的信号编号 
			sig = WTERMSIG(status); //获取使得进程退出的信号编号 

//			std::cout << "error in line " << __LINE__ << ", signal is " << sig << "\n";

			if (result == OJ_AC) {
				switch (sig) {
					case SIGCHLD :
					case SIGALRM :
						alarm(0);
					case SIGKILL :
					case SIGXCPU :
						result = OJ_TLE;
						break;
					case SIGXFSZ :
						result = OJ_OLE;
						break;
					default :
						result = OJ_RE;
				}
			}
			ptrace(PTRACE_KILL, pidRun, nullptr, nullptr);
			break;
		}

		// check invalid system call, x64 is ORIG_RAX*8, x86 is ORIG_EAX*4
		int sysCall = ptrace(PTRACE_PEEKUSER, pidRun, ORIG_RAX<<3, nullptr);
		//ptrace(PTRACE_PEEKUSR, pid, addr, data) 描述：从USER区域中读取一个字节，pid表示被跟踪的子进程，
		//USER区域地址由addr给出，data为用户变量地址用于返回读到的数据。USER结构为core文件的前面一部分，它描述了进程中止时的一些状态，如：寄存器值，代码、数据段大小，代码、数据段开始地址等
		if (!allowSysCall[sysCall]) {   
		//不允许的系统调用，则返回出错信息“运行时错误”
//			std::cout << "error in line " << __LINE__ << ", system call id is " << sysCall << "\n";

			result = OJ_RE;
			ptrace(PTRACE_KILL, pidRun, nullptr, nullptr); //  //给子进程发送 SIGKILL 信号，从而终止其执行,data，addr 参数被忽略。
			break;
		}

		ptrace(PTRACE_SYSCALL, pidRun, nullptr, nullptr);//如同 PTRACE_CONT 一样重启子进程的执行，
		//但指定子进程在下个入口或从系统调用退出时，或者执行单个指令后停止执行，这可用于实现单步调试。addr 参数被忽略。
	}

	if (result == OJ_TLE) usedTime = timeLimit;
	else {
		usedTime += (usage.ru_utime.tv_sec*1000 + usage.ru_utime.tv_usec/1000);
		usedTime += (usage.ru_stime.tv_sec*1000 + usage.ru_stime.tv_usec/1000);
	}
}

int checkResult(const char* DataOutFileName, const char* userOutFileName,int n)
{
	/* get user output data */
	std::ifstream fin1(userOutFileName);
	std::string line1;
	std::string userOut = "";

	while (getline(fin1,line1)) {
		userOut += line1+"\n";
	}

	fin1.close();

	std::ifstream fin2(DataOutFileName);
	std::string line2;
	std::string dataOut = "";

	while (getline(fin2,line2)) {
		dataOut += line2+"\n";
	}
         fin2.close();

        cout<<"dataOut: "<<dataOut<<"userOut: "<<userOut;

    ofstream outFile;
if (!outFile)
{
cout<<"no file"<<endl;
}
else{
    outFile.open("result.csv",ios::app); // 打开模式可省略  
} 

outFile<<"case "<<n+1<<": "<<"dataOut: "<<dataOut<<"case "<<n+1<<": "<<"userOut: "<<userOut;
//outFile<<"dataOut: "<<dataOut<<"userOut: "<<userOut;

outFile.close();
	

	/* compare */
	int dLen = dataOut.length();
	int uLen = userOut.length();

	int result = OJ_AC;
	if (uLen >= (dLen<<1)+OFFSET_OLE) result = OJ_OLE;	// output limit exceeded
	else if (uLen!=dLen || dataOut.compare(userOut)!=0) result = OJ_WA;

	return result;
}

int getFileSize(const char* fileName) {
	struct stat f_stat; //struct stat结构体:描述一个linux系统文件系统中的文件属性

	if (stat(fileName, &f_stat) == -1) return 0;
	else return f_stat.st_size;  //获取文件字节数
}

void initAllowSysCall(int lang) {   //初始化允许的系统调用，C、C++和Java内容不同
	memset(allowSysCall, false ,sizeof(allowSysCall));

	switch (lang) {
		case 1: case 2:
			for (int i=0; !i || ALLOW_SYS_CALL_C[i]; ++i) {
				allowSysCall[ALLOW_SYS_CALL_C[i]] = true;
			}
			break;
		case 3:
			for (int i=0; !i || ALLOW_SYS_CALL_JAVA[i]; ++i) {
				allowSysCall[ALLOW_SYS_CALL_JAVA[i]] = true;
			}
			break;
	}
}

